Lo crean o no, es posible hacer cálculos con datos encriptados. En otras palabras, es posible ejecutar un programa donde TODAS las variables están encriptadas!
En este tutorial, vamos a ir paso a paso por las herramientas más básicas de la computación encriptada. Específicamente, veremos un enfoque popular llamado Computación Segura Multi-Parte (Secure Multi-Party Computation). En esta lección, aprenderemos cómo construir una calculadora encriptada que puede hacer operaciones en números encriptados.
Autores:
Referencias:
Traductores:
A primera vista, CSMP es una manera extraña de "encriptación". En vez de usar una clave pública/privada para encriptar una variable, cada valor es dividido en múltiples acciones
, cada una funcionando como una clave privada. Típicamente, éstas acciones
serán distribuidas entre 2 o más dueños. Por lo tanto, para descifrar la variable, todos los dueños deben de estar de acuerdo en permitir el desciframiento. En esencia, cada persona tiene una clave privada.
Digamos que queremos "encriptar" una variable x
, podríamos hacerlo de la siguiente manera.
La encriptación no usa decimales o números reales, sino que ocurre en un espacio matemático llamado anillo cociente, el cual abarca los números enteros entre
0
yQ-1
, dondeQ
es un número primo y "suficientemente grande" para que el espacio pueda contener todos los números que usamos en nuestros experimentos. En la práctica, dado un número enterox
, nosotros hacemosx % Q
para encajar en el anillo. (Esa es la razón por la que evitamos usar un númerox' > Q
).
In [1]:
Q = 1234567891011
In [2]:
x = 25
In [3]:
import random
def encrypt(x):
share_a = random.randint(-Q,Q)
share_b = random.randint(-Q,Q)
share_c = (x - share_a - share_b) % Q
return (share_a, share_b, share_c)
In [4]:
encrypt(x)
Out[4]:
In [5]:
def decrypt(*shares):
return sum(shares) % Q
In [6]:
a,b,c = encrypt(25)
In [7]:
decrypt(a, b, c)
Out[7]:
Nótese que si tratamos de descifrar el valor de x
con solo dos acciones, el descifrado no funciona!
In [8]:
decrypt(a, b)
Out[8]:
Por lo tanto, necesitamos a todos los dueños para poder hacer el descifrado. De esta manera, las acciones
sirven como claves privadas, las cuales tienen que estar presente para descifrar un valor.
In [9]:
x = encrypt(25)
y = encrypt(5)
In [10]:
def add(x, y):
z = list()
# el primer trabajador suma sus acciones
z.append((x[0] + y[0]) % Q)
# el segundo trabajador suma sus acciones
z.append((x[1] + y[1]) % Q)
# el tercer trabajador suma sus acciones
z.append((x[2] + y[2]) % Q)
return z
In [11]:
decrypt(*add(x,y))
Out[11]:
Y aquí lo tienen! Si cada trabajador (de manera separada) suma sus acciones, las acciones resultantes van a descifrar el valor correcto (25 + 5 == 30).
Resulta que existen protocolos de CSMP que permiten esta computación encriptada para las siguientes operaciones:
y utilizando estas operaciones primitivas, podemos hacer computaciones arbitrarias!!
En la siguiente sección, vamos a aprender a cómo usar la librería PySyft para hacer tales operaciones!
En las secciones anteriores, describimos la intuición básica sobre como la CSMP debe de funcionar. Sin embargo, en la práctica no queremos escribir todas las operaciones primitivas nosotros cuando escribimos nuestros programas encriptados. En esta sección vamos a aprender lo básico sobre de la computación encriptada usando PySyft. Específicamente, nos vamos a enfocar en cómo ejecutar los 3 primitivos antes mencionados: adición, multiplicación y comparación.
Primero, necesitamos crear unos cuantos Trabajadores Virtuales (con los cuales esperemos que estén ya familiarizados dados nuestros tutoriales previos)
In [12]:
import torch
import syft as sy
hook = sy.TorchHook(torch)
bob = sy.VirtualWorker(hook, id="bob")
alice = sy.VirtualWorker(hook, id="alice")
bill = sy.VirtualWorker(hook, id="bill")
In [13]:
x = torch.tensor([25])
In [14]:
x
Out[14]:
In [15]:
encrypted_x = x.share(bob, alice, bill)
In [16]:
encrypted_x.get()
Out[16]:
In [17]:
bob._objects
Out[17]:
In [18]:
x = torch.tensor([25]).share(bob, alice, bill)
In [19]:
# Las acciones de Bob
bobs_share = list(bob._objects.values())[0]
bobs_share
Out[19]:
In [20]:
# Las acciones de Alice
alices_share = list(alice._objects.values())[0]
alices_share
Out[20]:
In [21]:
# Las acciones de Bill
bills_share = list(bill._objects.values())[0]
bills_share
Out[21]:
Y si quisiéramos, podríamos descifrar estos valores utilizando EL MISMO enfoque del que hablamos antes!
In [22]:
(bobs_share + alices_share + bills_share)
Out[22]:
Como pueden ver, cuando llamamos .share()
, simplemente partió el valor en 3 acciones y envió una acción a cada una de las partes!
In [23]:
x = torch.tensor([25]).share(bob,alice)
y = torch.tensor([5]).share(bob,alice)
In [24]:
z = x + y
z.get()
Out[24]:
In [25]:
z = x - y
z.get()
Out[25]:
Para la multiplicación necesitamos una parte adicional, quien es responsable de generar números aleatorios constantemente (y que no colude con ninguna de las otras partes). Denominamos a esta persona la "proveedora de encripción". Para nuestros propósitos, la proveedora de encripción es solo una VirtualWorker (Trabajadora Virtual) adicional, pero es importante reconocer que la proveedora no es una "dueña", en el sentido que ella no posee acciones propias pero es alguien en quien confiamos que no coludirá con los dueños existentes de las acciones.
In [26]:
crypto_provider = sy.VirtualWorker(hook, id="crypto_provider")
In [27]:
x = torch.tensor([25]).share(bob,alice, crypto_provider=crypto_provider)
y = torch.tensor([5]).share(bob,alice, crypto_provider=crypto_provider)
In [28]:
# multiplicación
z = x * y
z.get()
Out[28]:
También pueden hacer multiplicación de matrices
In [29]:
x = torch.tensor([[1, 2],[3,4]]).share(bob,alice, crypto_provider=crypto_provider)
y = torch.tensor([[2, 0],[0,2]]).share(bob,alice, crypto_provider=crypto_provider)
In [30]:
# multiplicación de matrices
z = x.mm(y)
z.get()
Out[30]:
Es también posible el hacer comparaciones privadas entre valores privados. En este caso contamos con el protocolo SecureNN, cuyos detalles pueden encontrar aquí. El resultado de la comparación es también un tensor compartido privado.
In [31]:
x = torch.tensor([25]).share(bob,alice, crypto_provider=crypto_provider)
y = torch.tensor([5]).share(bob,alice, crypto_provider=crypto_provider)
In [32]:
z = x > y
z.get()
Out[32]:
In [33]:
z = x <= y
z.get()
Out[33]:
In [34]:
z = x == y
z.get()
Out[34]:
In [35]:
z = x == y + 20
z.get()
Out[35]:
También pueden hacer operaciones de max.
In [36]:
x = torch.tensor([2, 3, 4, 1]).share(bob,alice, crypto_provider=crypto_provider)
x.max().get()
Out[36]:
In [37]:
x = torch.tensor([[2, 3], [4, 1]]).share(bob,alice, crypto_provider=crypto_provider)
max_values, max_ids = x.max(dim=0)
max_values.get()
Out[37]:
¡Felicitaciones por completar esta parte del tutorial! Si te gustó y quieres unirte al movimiento para preservar la privacidad, propiedad descentralizada de IA y la cadena de suministro de IA (datos), puedes hacerlo de las siguientes formas!
La forma más fácil de ayudar a nuestra comunidad es por darle estrellas a los repositorios de Github! Esto ayuda a crear consciencia de las interesantes herramientas que estamos construyendo.
La mejor manera de mantenerte actualizado con los últimos avances es ¡unirte a la comunidad! Tú lo puedes hacer llenando el formulario en http://slack.openmined.org
La mejor manera de contribuir a nuestra comunidad es convertirte en un ¡contribuidor de código! En cualquier momento puedes ir al Github Issues de PySyft y filtrar por "Proyectos". Esto mostrará todos los tiquetes de nivel superior dando un resumen de los proyectos a los que ¡te puedes unir! Si no te quieres unir a un proyecto, pero quieres hacer un poco de código, también puedes mirar más mini-proyectos "de una persona" buscando por Github Issues con la etiqueta "good first issue".
Si no tienes tiempo para contribuir a nuestra base de código, pero quieres ofrecer tu ayuda, también puedes aportar a nuestro Open Collective". Todas las donaciones van a nuestro web hosting y otros gastos de nuestra comunidad como ¡hackathons y meetups!
In [ ]: